#ifndef __GlDxSpotLight_h__
#define __GlDxSpotLight_h__

#include <d3d11.h>
#include <vector>
#include "GlDxGraphicComponent.h"
#include "GlSection.h"
#include "GlSpotLight.h"
#include "GlTriangle.h"

namespace GraphicLibrary
{
#	pragma warning(disable: 4250)

	class DxSprite;

	class DxSpotLight
		: public SpotLight
		, public DxGraphicComponent
	{
		public:
			DxSpotLight(ID3D11Device *device, const float angle, const float distance);
			virtual ~DxSpotLight();
			virtual Matrix4x4 getTransformationMatrix() const;
			virtual void apply(ID3D11DeviceContext *ic) const;
			inline UINT getIndexCount() const		{return mLighMesh.size() * 3;}
			static void Init(ID3D11Device *device)	{SpotLight::Init(new Factory(device));}
		private:
			class Factory
				: public SpotLight::Factory
			{
				public:
					inline Factory(ID3D11Device *device)
						: mDevice(device)
					{}
					virtual SpotLight* create(const float angle, const float distance);
				private:
					ID3D11Device*	mDevice;
			};
			class SectionData
			{
				public:
					const Position	mPosition;
					SectionArray	mSectionBegins;
					SectionArray	mSectionEnds;
					inline SectionData(const Position &pos)
						: mPosition(pos)
					{}
			};
			typedef std::vector<const DxSprite*>	DxSpriteArray;
			class SectionDataArray
				: public std::vector<SectionData*>
			{
				public:
					inline ~SectionDataArray()
					{
						for(iterator i = begin(); i != end(); ++i)
							delete *i;
					}
			};

			mutable ID3D11Buffer*	mVertexBuffer;
			mutable ID3D11Buffer*	mIndexBuffer;
			ID3D11Device*			mDevice;

			mutable Triangle::TriangleArray	mLighMesh;

			void generateLightMesh() const;
			void createBuffers(ID3D11Device *device) const;
			inline float getU(const Position &pos) const	{return pos.mX / mDistance;}
			inline float getV(const Position &pos) const	{return pos.mY / mDistance;}
			void fillSpritesInRange(DxSpriteArray &sprites) const;
			void removeSpriteShadow(const Triangle::TriangleArray &source, const DxSprite *sprite, Triangle::TriangleArray &result, const Matrix4x4 &invTransMat) const;
			void removeSpriteShadow(const Triangle &source, const DxSprite *sprite, Triangle::TriangleArray &result, const Matrix4x4 &invTransMat) const;
			void removeSectionShadow(const Triangle::TriangleArray &source, const Section &section, Triangle::TriangleArray &result) const;
			void removeSectionShadow(const Triangle &triangle, const Section &section, Triangle::TriangleArray &result) const;
			void removeSectionShadow_withoutIntersection(const Triangle &triangle, const Section &section, Triangle::TriangleArray &result, const bool force = false) const;
			void removeSectionShadow_oneIntersection(const Triangle &triangle, const Section &section, Triangle::TriangleArray &result, const Position *iPositions, const bool *intersects) const;
			void removeSectionShadow_twoIntersection(const Triangle &triangle, const Section &section, Triangle::TriangleArray &result, const Position *iPositions, const bool *intersects) const;
			void insertToTriangleArray(Triangle::TriangleArray &result, const Triangle &triangle) const;
			Section createExtendedSection(const Position &begin , const Position &end) const;
			Position getProjection(const Position &point, const Position &origin, const Section &surface) const;
			Matrix4x4 getInvTransformationMatrix() const;
			void fillSectionArrayWithSpriteBoundingBox(const Triangle &triangle, const DxSpriteArray &sprites, SectionDataArray &sections) const;
			void fillLighMesh(const Triangle &triangle, const SectionDataArray &sections, Triangle::TriangleArray &lightMesh) const;
			SectionDataArray::iterator findPlace(SectionDataArray &sections, const Position &pos) const;
			void add(const Triangle &triangle, SectionDataArray &sections, Section section) const;
			bool setSectionToTriangle(const Triangle &triangle, Section &section, Position &beginProjection, Position &endProjection) const;
			void findLowests(const Triangle &triangle, const SectionArray &sections, const Position &projection, SectionArray &result) const;
			float getHeightAt(const Triangle &triangle, const Section &section, const Position &projection) const;
			Position getPositionAt(const Triangle &triangle, const Section &section, const Position &projection) const;
	};

#	pragma warning(default: 4250)
}

#endif // __GlSpotLight_h__
